package nbp.currency.logic;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Scanner;

import nbp.currency.gui.TableColumnEnum;

import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

public class NBPService {

	private static final String CONFIG_PROPERTIES = "config.properties";
	private static NBPService service;
	private static String URL_NBP;
	private static String URL_DIR_FILE;
	private static String RESOURCE_PATH;
	private static String EXTENSION;
	private static String DATE;
	private static String CHILDREN;

	private Map<TypeOfExchange, Map<String, String>> filesName;
	private static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyMMdd");

	private NBPService() throws IOException {

		Properties properties = getProperties();
		URL_NBP = properties.getProperty("nbp.url");
		URL_DIR_FILE = properties.getProperty("nbp.url.dir");
		RESOURCE_PATH = properties.getProperty("resource.path");
		EXTENSION = properties.getProperty("resource.extension");
		DATE = properties.getProperty("xml.date.node");
		CHILDREN = properties.getProperty("xml.children.node");

		validProperties();

	};

	private boolean validProperties() throws IOException {
		if (URL_NBP != null && isNBPUrlRunning()) {
			if (URL_DIR_FILE != null && RESOURCE_PATH != null && EXTENSION != null) {
				return true;
			}
		}
		return false;
	}

	private Properties getProperties() throws IOException {
		Properties properties = new Properties();
		InputStream input = null;

		input = new FileInputStream(CONFIG_PROPERTIES);
		properties.load(input);

		if (input != null) {
			input.close();
		}
		return properties;
	}

	public static synchronized NBPService getInstance() throws IOException {
		if (service == null) {
			service = new NBPService();
		}
		return service;
	}

	public List<CurrencyWrapper> getCurrency(Date to, Date from, TypeOfExchange tableType) throws JDOMException, IOException, ParseException {

		List<CurrencyWrapper> currencyWrappers = new ArrayList<CurrencyWrapper>();

		if (!isDirectoryInResource("")) {
			makeDirectories();
		}

		Calendar fromCalendar = GregorianCalendar.getInstance();
		fromCalendar.setTime(from);

		while (fromCalendar.getTime().compareTo(to) <= 0) {

			String date = DATE_FORMAT.format(fromCalendar.getTime());
			String fileName = tableType.getTableType() + File.separator + date + EXTENSION;
			if (!isFileInResource(fileName)) {
				getXMLFileFromNBP(date, tableType);
			}

			if (isFileInResource(fileName)) {
				currencyWrappers.addAll(parseXML(RESOURCE_PATH + fileName));
			}

			fromCalendar.add(Calendar.DAY_OF_YEAR, 1);
		}

		return currencyWrappers;

	}

	private void getXMLFileFromNBP(String date, TypeOfExchange tableType) {
		String string = getFilesNameMap().get(tableType).get(date);
		if (string != null) {
			copyFileFromNBP(string, tableType, date);
		}
	}

	public static void main(String[] args) throws IOException {
	}

	public boolean isNBPUrlRunning() throws IOException {
		URL u = new URL(URL_NBP);
		HttpURLConnection huc = (HttpURLConnection) u.openConnection();
		huc.setRequestMethod("GET");
		huc.connect();
		return huc.getResponseCode() == HttpURLConnection.HTTP_OK;
	}

	private Collection<? extends CurrencyWrapper> parseXML(String fileName) throws JDOMException, IOException, ParseException {
		List<CurrencyWrapper> result = new ArrayList<CurrencyWrapper>();
		SAXBuilder jdomBuilder = new SAXBuilder();
		Document jdomDocument = jdomBuilder.build(fileName);
		Element rss = jdomDocument.getRootElement();
		String dateText = rss.getChildText(DATE);
		Date date = null;
		date = new SimpleDateFormat("yyyy-MM-dd").parse(dateText);
		List<Element> channel = rss.getChildren(CHILDREN);

		for (Element element : channel) {
			CurrencyWrapper createCurrencyWrapper = createCurrencyWrapper(element, date);
			result.add(createCurrencyWrapper);
		}

		return result;
	}

	private CurrencyWrapper createCurrencyWrapper(Element currency, Date date) {
		if (currency != null) {
			CurrencyWrapper currencyWrapper = new CurrencyWrapper();
			currencyWrapper.setDate(date);
			currencyWrapper.setConverter(currency.getChildText(TableColumnEnum.CONVERTER.getXmlName()));
			currencyWrapper.setCurrencyCode(currency.getChildText(TableColumnEnum.CODE.getXmlName()));
			String averageText = currency.getChildText(TableColumnEnum.AVERAGE_EXCHANGE.getXmlName());
			BigDecimal average = getBigDecimalFromString(averageText);
			if (average != null) {
				currencyWrapper.setAverageExchange(average);
			}

			String buyText = currency.getChildText(TableColumnEnum.BUY_EXCHANGE.getXmlName());
			BigDecimal buy = getBigDecimalFromString(buyText);
			if (buyText != null) {
				currencyWrapper.setBuyExchange(buy);
			}

			String sellText = currency.getChildText(TableColumnEnum.SELL_EXCHANGE.getXmlName());
			BigDecimal sell = getBigDecimalFromString(sellText);
			if (sell != null) {
				currencyWrapper.setSellExchange(sell);
			}
			return currencyWrapper;
		}
		return null;
	}

	private void copyFileFromNBP(String fileName, TypeOfExchange tableType, String copyFileName) {
		InputStream inStream = null;
		OutputStream outStream = null;
		try {
			URL url = new URL(URL_NBP + fileName);

			inStream = url.openStream();
			outStream = new FileOutputStream(RESOURCE_PATH + tableType.getTableType() + File.separator + copyFileName + EXTENSION);

			byte[] buffer = new byte[1024];
			int length;
			while ((length = inStream.read(buffer)) > 0) {
				outStream.write(buffer, 0, length);
			}

			inStream.close();
			outStream.flush();
			outStream.close();

		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private boolean isFileInResource(String fileName) {
		File file = new File(RESOURCE_PATH + fileName);
		if (file.exists() && !file.isDirectory()) {
			return true;
		}
		return false;
	}

	private boolean isDirectoryInResource(String directoryName) {
		File file = new File(RESOURCE_PATH + directoryName);
		if (file.exists() && file.isDirectory()) {
			return true;
		}
		return false;
	}

	private void makeDirectories() {

		File average = new File(RESOURCE_PATH + TypeOfExchange.AVERAGE_FOREIGN.getTableType());
		File averageUnconvertible = new File(RESOURCE_PATH + TypeOfExchange.AVERAGE_INCONVERTIBLE.getTableType());
		File buySell = new File(RESOURCE_PATH + TypeOfExchange.PURCHASE_SALE.getTableType());
		average.mkdirs();
		averageUnconvertible.mkdirs();
		buySell.mkdirs();

	}

	private Map<TypeOfExchange, Map<String, String>> getFilesNameMap() {

		if (filesName == null) {
			URL url;
			InputStream inputStream;
			filesName = new HashMap<TypeOfExchange, Map<String, String>>();
			for (TypeOfExchange type : TypeOfExchange.values()) {
				filesName.put(type, new HashMap<String, String>());
			}

			try {
				url = new URL(URL_DIR_FILE);
				inputStream = url.openStream();
				Scanner scanner = new Scanner(inputStream, "UTF-8");
				while (scanner.hasNextLine()) {
					String fileName = scanner.nextLine();

					if (fileName.startsWith("\uFEFF")) {
						fileName = fileName.substring(1);
					}

					String type = String.valueOf(fileName.charAt(0));
					String date = getDate(fileName);
					TypeOfExchange t = TypeOfExchange.getByType(type);
					if (t != null) {
						filesName.get(TypeOfExchange.getByType(type)).put(date, fileName + EXTENSION);
					}
				}
				scanner.close();
			} catch (Exception e) {
				filesName = new HashMap<TypeOfExchange, Map<String, String>>();
			}
		}

		return filesName;
	}

	private BigDecimal getBigDecimalFromString(String text) {
		if (text != null) {
			return new BigDecimal(text.replace(",", "."));
		}
		return null;
	}

	private String getDate(String text) throws Exception {
		String substring = text.substring(5);
		return substring;
	}
}
